// Ο μετρητής "feredataID" μετρά τα πάσης φύσεως αιτήματα feredata και
// αποστέλλεται στον skiser με σκοπό να παραληφθεί εκ νέου μαζί με τα όποια
// αποτελέσματα. Κατά την παραλαβή ελέγχεται και αν βρεθεί παρωχημένος
// τα αποτελέσματα αγνοούνται καθώς αυτό σημαίνει ότι έχει ήδη υποβληθεί
// νεότερο αίτημα feredata.

Skiniko.feredataID = 0;

// Το στήσιμο του σκηνικού στον client γίνεται με αίτημα feredata για πλήρη
// σκηνικά δεδομένα. Αμέσως μετά την παραλαβή των δεδομένων μπορεί να κληθεί
// callback function.

Skiniko.prototype.stisimo = function(callback) {
	var skiniko = this;

	if (Debug.flagGet('feredata')) console.log('Ζητήθηκαν πλήρη σκηνικά δεδομένα');
	Skiniko.feredataID++;
	Client.skiserService('fereFreska', 'id=' + Skiniko.feredataID).
	done(function(rsp) {
		if (Debug.flagGet('feredata')) {
			try {
				console.groupCollapsed('Παρελήφθησαν πλήρη σκηνικά δεδομένα');
			} catch (e) {
				console.log('Παρελήφθησαν πλήρη σκηνικά δεδομένα');
			}
			console.log(rsp);
			try {
				console.groupEnd();
			} catch(e) {}
		}

		try {
			eval('var data = {' + rsp + '};');
		} catch(e) {
			skiniko.feredataError('Παρελήφθησαν λανθασμένα σκηνικά δεδομένα', rsp);
			return;
		}

		if (!data.hasOwnProperty('id')) {
			skiniko.feredataError('Ακαθόριστο ID πακέτου σκηνικών δεδομένων', rsp);
			return;
		}

		if (data.id !== Skiniko.feredataID) return;

		if (skiniko.hasOwnProperty('feredataErrorCount')) {
			Client.fyi.pano();
			delete skiniko.feredataErrorCount;
		}

		// Αν κάνω prepend στις περιοχές των θαμώνων και των τραπεζιών έχω κάποιο πρόβλημα
		// ειδικά στον Chrome, δεν γνωρίζω γιατί. Επομένως, προτιμώ να βάλω ένα κενό DOM
		// element στο επάνω μέρος της περιοχής και να προσθέτω τους νέους θαμώνες και τα
		// νέα τραπέζια μετά από αυτόν τον κόμβο. Το ίδιο ισχύεει και για τις προσκλήσεις.

		Kafenio.thamonesDOM.empty().append(Kafenio.thamonesBaseDOM = $('<div>'));
		Kafenio.trapeziaDOM.empty().append(Kafenio.trapeziaBaseDOM = $('<div>'));
		Kafenio.prosklisiDOM.empty().append(Kafenio.prosklisiBaseDOM = $('<div>'));
		Kafenio.sizitisiDOM.empty().append(Kafenio.sizitisiProepiskopisiDOM = $('<div>').
			addClass('sizitisiProepiskopisi'));

		Globals.awalk(data.pektis, function(i, pektis) {
			pektis = new Pektis(pektis);
			skiniko.skinikoPektisSet(pektis);
		});

		Globals.walk(data.peparam, function(pektis, peparam) {
			pektis = skiniko.skinikoPektisGet(pektis);
			if (pektis) pektis.peparam = peparam;
		});

		Globals.awalk(data.sinedria.sort(function(s1, s2) {
			if (s1.isodos < s2.isodos) return -1;
			if (s1.isodos > s2.isodos) return 1;
			return 0;
		}), function(i, sinedria) {
			sinedria = new Sinedria(sinedria);
			skiniko.skinikoSinedriaSet(sinedria);
		});

		Kafenio.ego = Kafenio.skiniko.skinikoSinedriaGet(Client.session.pektis);
		if (!Kafenio.ego) Client.error('Δεν βρέθηκε συνεδρία παίκτη');

		Kafenio.egoPektis = Kafenio.skiniko.skinikoPektisGet(Client.session.pektis);
		if (!Kafenio.egoPektis) Client.error('Δεν βρέθηκε παίκτης συνεδρίας στο σκηνικό');

		Kafenio.egoPektis.sxesi = data.sxesi;

		skiniko.skinikoSinedriaWalk(function() {
			this.sinedriaCreateDOM();
		});

		Globals.awalk(data.trapezi, function(i, trapezi) {
			trapezi = new Trapezi(trapezi);
			skiniko.skinikoTrapeziSet(trapezi);
			trapezi.trapeziThesiWalk(function(thesi) {
				var pektis = this.trapeziPektisGet(thesi);
				if (pektis) this.trapeziIxotemisSet(thesi, pektis);
			});
		});

		Globals.awalk(data.dianomi, function(i, dianomi) {
			var trapezi;

			dianomi = new Dianomi(dianomi);
			trapezi = skiniko.skinikoTrapeziGet(dianomi.dianomiTrapeziGet());
			if (!trapezi) return;
			trapezi.trapeziDianomiSet(dianomi);
			trapezi.dianomiArray.push(dianomi);
		});

		Globals.awalk(data.trapezi.sort(function(t1, t2) {
			if (t1.kodikos < t2.kodikos) return -1;
			if (t1.kodikos > t2.kodikos) return 1;
			return 0;
		}), function(i, trapezi) {
			trapezi = skiniko.skinikoTrapeziGet(trapezi.kodikos);
			if (!trapezi) return;
			trapezi.trapeziCreateDOM();
		});

		Globals.awalk(data.prosklisi.sort(function(p1, p2) {
			if (p1.kodikos < p2.kodikos) return -1;
			if (p1.kodikos > p2.kodikos) return 1;
			return 0;
		}), function(i, prosklisi) {
			prosklisi = new Prosklisi(prosklisi);
			skiniko.skinikoProsklisiSet(prosklisi);
			prosklisi.prosklisiCreateDOM();
		});

		Globals.awalk(data.sizitisi, function(i, sizitisi) {
			sizitisi = new Sizitisi(sizitisi);
			skiniko.skinikoSizitisiSet(sizitisi);
			sizitisi.sizitisiCreateDOM(Kafenio);
		});

		skiniko.
		pektisEntopismosDOM(Client.session.pektis).
		pektisTrapeziScroll().
		feredataResetCheck(data).
		feredataTsoxaDataFix(data).
		feredataTsoxa(data.tsoxa);

		if (Kafenio.sizitisiDOM.data('pagomeno')) Kafenio.sizitisiDOM.css({visibility: 'visible'});
		else Kafenio.sizitisiDOM.scrollKato(1, function() {
			Kafenio.sizitisiDOM.css({visibility: 'visible'});
			Kafenio.sizitisiDOM.scrollKato();
		});
		Kafenio.thamonesDOM.css({visibility: 'visible'});
		Kafenio.trapeziaDOM.css({visibility: 'visible'});
		Kafenio.panelRefresh();

		if (callback) callback();
		skiniko.feredata();
	}).
	fail(function(err) {
		skiniko.feredataError(err);
	});

	return this;
};

Skiniko.prototype.feredata = function() {
	var skiniko = this;

	if (Debug.flagGet('feredata')) console.log('Ζητήθηκαν μεταβολές');
	Skiniko.feredataID++;
	Client.skiserService('fereAlages', 'id=' + Skiniko.feredataID).
	done(function(rsp) {
		switch (rsp) {

		// Δεν έχει αλλάξει τίποτα, το αίτημα feredata έληξε και θα πρέπει
		// να δρομολογηθεί νέο.

		case '=':
			if (Debug.flagGet('feredata')) console.log('\t' + rsp, '(δεν άλλαξε κάτι)');
			return skiniko.feredata();

		// Το αίτημα feredata κατέστη παρωχημένο λόγω υποβολής νεότερου αιτήματος
		// feredata.

		case '~':
			if (Debug.flagGet('feredata')) console.log('\t' + rsp, '(υποβλήθηκε νεότερο αίτημα)');
			return;

		// Ο skiser διατάσσει έξοδο του παρόντος client. Αυτό μπορεί να συμβεί
		// είτε κατά την ρητή έξοδο του παίκτη, είτε κατά την επανείσοδο μέσω
		// νέας συνεδρίας.

		case '_':
			if (Debug.flagGet('feredata')) console.log('\t' + rsp, '(πραγματοποιήθηκε έξοδος)');
			$.ajax('account/exodos.php', {async: false});
			return self.location = Client.server;

		// Ο skiser μπορεί να διακόψει την επικοινωνία είτε επειδή παρήλθε πολύς
		// χρόνος απραξίας για κάποια συνεδρία, είτε επειδή με κάποιον τρόπο
		// ο παίκτης κατάφερε να επανεισέλθει χωρίς επανείσοδο, π.χ. ανοίγοντας
		// νέα καρτέλα της εφαρμογής από τον ίδιο browser.

		case '-':
			if (Debug.flagGet('feredata')) console.log('\t' + rsp, '(διακοπή της επικοινωνίας)');
			return Client.error('Ο server σκηνικού διέκοψε την επικοινωνία');

		// Παρουσιάστηκε κάποιο πρόβλημα στην ομαλή ενημέρωση σκηνικών δεδομένων
		// και θα πρέπει να δρομολογηθεί νέος κύκλος εκκινώντας με πλήρη σκηνικά
		// δεδομένα.

		case '?':
			return skiniko.feredataError('κρίθηκε αναγκαία η πλήρης επανασύσταση του σκηνικού');
		}

		// Έχει επιστραφεί array κινήσεων. Πρόκειται για μεταβολές που επήλθαν
		// μετά την τελευταία παραλαβή feredata.

		if (Debug.flagGet('feredata')) {
			try {
				console.groupCollapsed('Παρελήφθησαν μεταβολές');
			} catch(e) {
				console.log('Παρελήφθησαν μεταβολές');
			}
			console.log(rsp);
			try {
				console.groupEnd();
			} catch(e) {}
		}

		try {
			eval('var data = {' + rsp + '};');
		} catch(e) {
			skiniko.feredataError('Παρελήφθησαν λανθασμένες κινήσεις', rsp);
			return;
		}

		if (!data.hasOwnProperty('id')) {
			skiniko.feredataError('Ακαθόριστο ID πακέτου μεταβολών', rsp);
			return;
		}
		if (data.id !== Skiniko.feredataID) return;

		skiniko.
		feredataKinisiDataFix(data).
		feredataKinisi(data.kinisi).
		feredataTsoxaDataFix(data).
		feredataTsoxa(data.tsoxa, true).
		feredata();
	}).
	fail(function(err) {
		skiniko.feredataError(err);
	});

	return this;
};

// Η μέθοδος "feredataKinisiDataFix" δέχεται μια αναφορά στα παραληφθέντα δεδομένα
// και διασφαλίζει ότι σε αυτά θα υπάρχει array "kinisi" με τις νέες κινήσεις τού
// transaction log. Αυτό το κάνουμε για να μην ρωτάμε συνεχώς αν υπάρχουν ή όχι
// δεδομένα κίνησης κλπ.
//
// Δεύτερος και πιο ουσιαστικός λόγος είναι να μετατραπούν οι κινήσεις σε αντικείμενα
// κίνησης ώστε να είναι εύκολα διαχειρίσειμες με τις διαθέσιμες μεθόδους κινήσεων.
//
// Όσον αφορά τη διάταξη των στοιχείων δεν χρειάζεται να γίνει κάποια ενέργεια καθώς
// οι κινήσεις είναι ήδη ταξινομημένες σε αντίθεση με τις συζητήσεις και τις ενέργειες
// της τσόχας που έρχονται με τυχαία σειρά.

Skiniko.prototype.feredataKinisiDataFix = function(data) {
	if (!data.kinisi) data.kinisi = [];
	else if (data.kinisi.length > 0) {
		Globals.awalk(data.kinisi, function(i, kinisi) {
			data.kinisi[i] = new Kinisi(kinisi);
		});
	}

	return this;
};

// Η μέθοδος "feredataKinisi" δέχεται το array των παραληφθεισών κνίσεων και το διατρέχει
// με τη σειρά εφαρμόζοντας τις κινήσεις αυτές στο σκηνικό.

Skiniko.prototype.feredataKinisi = function(kinisiData) {
	var skiniko = this;

	Globals.awalk(kinisiData, function(i, kinisi) {
		skiniko.processKinisi(kinisi);
	});

	return this;
};

// Όταν κάνουμε restart τον skiser οι ενεργές συνεδρίες λαμβάνουν φρέσκα σκηνικά δεδομένα
// στα οποία υπάρχει και attribute "reset". Η μέθοδος "feredataResetCheck" ελέγχε ακριβώς
// αυτό και αν βρεί attribute "reset" επαναδιαμορφώνει την τσόχα εφόσον αυτή είναι ενεργή.

Skiniko.prototype.feredataResetCheck = function(data) {
	if (!data.reset) return this;

	Client.fyi.pano('Επαναδιαμορφώθηκε το σκηνικό του καφενείου');
	if (Kafenio.oxiTsoxa()) return this;

	Kafenio.tsoxa.Tsoxa.refreshDOM(true);
	return this;
};

// Η μέθοδος "tsoxaDataFix" δέχεται μια αναφορά σατ παραληφθέντα δεδομένα και τα διορθώνει
// όσον αφορά στα δεδομένα τσόχας. Πιο συγκεκριμένα, διασφαλίζει την ύπαρξη των δεδομένων
// τσόχας και πιο συγκεκριμένα την ύπαρξη array συζήτησης και array ενεργειών.
//
// Κατά δεύτερον μετατρέπει τα arrays αυτά σε arrays αντικειμένων συζήτησης και ενεργειών
// αντίστοιχα.
//
// Τέλος, ταξινομεί τα arrays ως προς τον κωδικό οπότε τα στοιχεία θα  προσπελαστούν με
// χρονική σειρά.

Skiniko.prototype.feredataTsoxaDataFix = function(data) {
	if (!data.tsoxa) data.tsoxa = {};

	if (!data.tsoxa.sizitisi) data.tsoxa.sizitisi = [];
	else if (data.tsoxa.sizitisi.length > 0) {
		Globals.awalk(data.tsoxa.sizitisi, function(i, sizitisi) {
			data.tsoxa.sizitisi[i] = new Sizitisi(sizitisi);
		});

		data.tsoxa.sizitisi.sort(function(s1, s2) {
			var k1 = s1.sizitisiKodikosGet(), k2 = s2.sizitisiKodikosGet();

			if (k1 < k2) return -1;
			if (k1 > k2 ) return 1;
			return 0;
		});
	}

	if (!data.tsoxa.energia) data.tsoxa.energia = [];
	else if (data.tsoxa.energia.length > 0) {
		Globals.awalk(data.tsoxa.energia, function(i, energia) {
			data.tsoxa.energia[i] = new Energia(energia);
		});

		data.tsoxa.energia.sort(function(s1, s2) {
			var k1 = s1.energiaKodikosGet(), k2 = s2.energiaKodikosGet();

			if (k1 < k2) return -1;
			if (k1 > k2 ) return 1;
			return 0;
		});
	}

	return this;
};

Skiniko.prototype.feredataTsoxa = function(data, online) {
	var trapezi;

	if (!data) return this;

	trapezi = Kafenio.egoTrapezi();
	if (!trapezi) return this;

	trapezi.
	feredataTsoxaSizitisi(data.sizitisi).
	feredataTsoxaEnergia(data.energia, online);

	return this;
};

Trapezi.prototype.feredataTsoxaSizitisi = function(sizitisiData) {
	var trapezi = this;

	Globals.awalk(sizitisiData, function(i, sizitisi) {
		trapezi.trapeziSizitisiSet(sizitisi);
		if (Kafenio.oxiTsoxa()) return;

		sizitisi.sizitisiCreateDOM(Tsoxa);
		if (sizitisi.sizitisiPektisGet().isEgo())
		Tsoxa.sizitisiProepiskopisiDOM.empty().removeClass('sizitisi');
	});

	if (Kafenio.oxiTsoxa()) return this;

	if (!Tsoxa.sizitisiDOM.data('pagomeno')) Tsoxa.sizitisiDOM.scrollKato();
	return this;
};

Trapezi.prototype.feredataTsoxaEnergia = function(energiaData, online) {
	var trapezi = this, procOnline, allOnline;

	allOnline = online;
	Globals.awalk(energiaData, function(i, energia) {
		var dianomi = trapezi.trapeziDianomiGet(energia.energiaDianomiGet());
		if (!dianomi) return;

		dianomi.dianomiEnergiaSet(energia);
		dianomi.energiaArray.push(energia);
		if (!online) return;
		if (!allOnline) return;

		procOnline = 'processEnergiaOnline' + energia.energiaIdosGet();
		if (typeof trapezi[procOnline] !== 'function') allOnline = false;
	});

	this.partidaReplay();
	if (Kafenio.oxiTsoxa()) return this;

	if (!allOnline) {
		Tsoxa.
		trapeziRefreshDOM().
		panelRefresh();
		return this;
	}

	Globals.awalk(energiaData, function(i, energia) {
		var dianomi = trapezi.trapeziDianomiGet(energia.energiaDianomiGet());
		if (!dianomi) return;

		procOnline = 'processEnergiaOnline' + energia.energiaIdosGet();
		if (typeof trapezi[procOnline] !== 'function') return;
		trapezi[procOnline](energia);
	});

	return this;
};

// Σε περίπτωση προβλήματος επικοινωνίας σε αίτημα feredata επιχειρείται επαναστήσιμο
// του σκηνικού. Αυτή η διαδικασία επαναλαμβάνεται με χρονική καθυστέρηση η οποία
// σιγά σιγά αυξάνει και μετά από εύλογο χρονικό διάστημα προκαλεί έξοδο από το
// πρόγραμμα.

Skiniko.prototype.feredataError = function(err, rsp) {
	var skiniko = this, delay;

// XXX
// Το άμεσο "return" που ακολουθεί πρέπει να σχολιαστεί.
/*
return;
*/
	if (!this.hasOwnProperty('feredataErrorCount')) {
		console.error(rsp);
		Client.skiserFail(err);
		this.feredataErrorCount = 1;
		delay = 100;
	}
	else if (this.feredataErrorCount++ < 5) delay = 200;
	else if (this.feredataErrorCount < 10) delay = 500;
	else if (this.feredataErrorCount < 15) delay = 1000;
	else return Client.error('feredata error');

	setTimeout(function() {
		skiniko.stisimo();
	}, delay);
};

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Sinedria.prototype.sinedriaCreateDOM = function() {
	var sinedria = this, pektis = this.sinedriaPektisGet();

	this.sinedriaDeleteDOM();
	Kafenio.thamonesBaseDOM.after(
	this.DOM = Pektis.pektisLoginDOM(pektis).
	on('click', function(e) {
		sinedria.sinedriaProfinfoDOM($(this), Kafenio);
	}));

	if (this.sinedriaTrapeziGet()) this.DOM.addClass(this.sinedriaIsPektis() ? 'pektis' : 'theatis');
	return this;
};

Sinedria.prototype.sinedriaDeleteDOM = function() {
	if (this.hasOwnProperty('DOM')) this.DOM.remove();
	delete this.DOM;
	return this;
};

Sinedria.prototype.sinedriaTheatisDeleteDOM = function() {
	var trapezi, skiniko;

	if (this.sinedriaOxiTheatis()) return this;
	skiniko = this.sinedriaSkinikoGet();
	if (!skiniko) return this;

	trapezi = skiniko.skinikoTrapeziGet(this.sinedriaTrapeziGet());
	if (!trapezi) return this;

	trapezi.trapeziTheatisDeleteDOM(this.sinedriaPektisGet());
	return this;
};

Sinedria.prototype.sinedriaGetDOM = function() {
	return this.DOM;
};

Sinedria.prototype.sinedriaTsoxaTheatisCreateDOM = function() {
	var sinedria = this, pektis = this.sinedriaPektisGet();

	this.sinedriaTsoxaTheatisDeleteDOM();
	Tsoxa.theatesBaseDOM.after(
	this.tsoxaTheatisDOM = Pektis.pektisLoginDOM(pektis).addClass('theatis tsoxa_theatis').
	on('click', function(e) {
		sinedria.sinedriaProfinfoDOM($(this), Tsoxa, Kafenio.tsoxa);
	}));
	if (pektis.isEgo()) this.tsoxaTheatisDOM.addClass('ego');

	return this;
};

Sinedria.prototype.sinedriaTsoxaTheatisDeleteDOM = function() {
	if (this.hasOwnProperty('tsoxaTheatisDOM')) this.tsoxaTheatisDOM.remove();
	delete this.tsoxaTheatisDOM;
	return this;
};

Sinedria.prototype.sinedriaTsoxaTheatisGetDOM = function() {
	return this.tsoxaTheatisDOM;
};

Sinedria.prototype.sinedriaProfinfoDOM = function(domel, xoros, win) {
	var sinedria = this, login = this.sinedriaPektisGet(), skiniko, pektis,
		domelOffset, ofelimoOffset, ofelimoHeight, dif;

	skiniko = this.sinedriaSkinikoGet();
	if (!skiniko) return this;

	pektis = skiniko.skinikoPektisGet(login);
	if (!pektis) return this;

	if (xoros.profinfoDOM.hasOwnProperty(login)) {
		xoros.profinfoDOM[login].remove();
		return this;
	}

	if (!xoros.profinfoOffset.hasOwnProperty(login)) {
		ofelimoOffset = xoros.ofelimoDOM.offset();
		ofelimoHeight = xoros.ofelimoDOM.outerHeight();
		domelOffset = domel.offset();
		domelOffset.top -= ofelimoOffset.top + Globals.random(-20, 20);
		dif = ofelimoHeight - domelOffset.top - 400;
		if (dif < 100) domelOffset.top += dif - Globals.random(0, 100);
		if (domelOffset.top < 0) domelOffset.top = Globals.random(0, 20);
		xoros.profinfoOffset[login] = {
			top: domelOffset.top + 'px',
			left: (domelOffset.left - ofelimoOffset.left + 120 + Globals.random(-20, 20)) + 'px',
		};
	}

	pektis.pektisProfinfoDOM(xoros, win);
	return this;
};

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Trapezi.prototype.trapeziGetDOM = function() {
	return this.DOM;
};

Trapezi.prototype.trapeziCreateDOM = function() {
	var trapezi = this, kodikos = this.trapeziKodikosGet(), dom, ipolipo;

	dom = this.trapeziGetDOM();
	if (dom) {
		this.DOM = dom.empty();
	}
	else {
		Kafenio.trapeziaBaseDOM.
		after(this.DOM = $('<div>').addClass('trapezi').
		on('click', function(e) {
			Kafenio.inputRefocus(e);

			// Αν ο χρήστης κάνει κλικ στο τραπέζι που ήδη έχει επιλέξει,
			// τότε εμφανίζεται η τσόχα.

			if (Kafenio.ego.sinedriaTrapeziGet() === kodikos) {
				if (Kafenio.isTsoxa()) Kafenio.tsoxaFocus();
				else Kafenio.tsoxaActivate();
				return trapezi;
			}

			trapezi.trapeziEkremotitaDOM();
			Client.skiserService('trapeziEpilogi', 'trapezi=' + trapezi.trapeziKodikosGet()).
			done(function(rsp) {
				trapezi.trapeziEkremotitaDOM(false);
			}).
			fail(function(err) {
				trapezi.trapeziEkremotitaDOM(false);
				Client.skiserFail(err);
			});
		}));
	}

	this.thesiDOM = {};
	this.theatisDOM = {};
	ipolipo = this.trapeziIpolipoGet();

	this.DOM.
	append(this.optionsDOM = $('<div>').addClass('trapeziOptions')).

	append($('<div>').addClass('trapeziData').
	append($('<div>').addClass('trapeziDataKodikos').
	attr('title', 'Κωδικός τραπεζιού').text(this.trapeziKodikosGet())).

	append($('<div>').addClass('trapeziDataIpolipo').
	attr('title', 'Τρέχον υπόλοιπο κάσας').text(ipolipo / 10))).

	append(this.thesiCreateDOM(1)).
	append(this.thesiCreateDOM(2)).
	append(this.thesiCreateDOM(3)).

	append(
		this.theatesDOM = $('<div>').addClass('trapeziTheates').attr({
			title: 'Περιοχή θεατών',
		})
	);

	if (ipolipo <= 0) this.DOM.append($('<img>').addClass('telosIcon').attr('src', 'ikona/endixi/telos.png'));
	else this.DOM.find('.telosIcon').remove();

	this.trapeziOptionsRefreshDOM();
	this.trapeziTheatisRefreshDOM();
	return this;
};

Trapezi.prototype.trapeziOptionsRefreshDOM = function() {
	var dom;

	dom = this.optionsDOM.empty();
	if (this.trapeziOxiAsoi()) this.trapeziOptionDOM('Δεν παίζονται οι άσοι', 'asoiOn.png');
	if (this.trapeziIsPaso()) this.trapeziOptionDOM('Παίζεται το πάσο', 'pasoOn.png');
	return this;
};

Trapezi.prototype.trapeziOptionDOM = function(desc, img) {
	this.optionsDOM.append($('<img>').addClass('trapeziOption').attr({
		title: desc,
		src: Client.server + 'ikona/panel/pexnidi/' + img,
	}));
	return this;
};

Trapezi.prototype.trapeziTheatisRefreshDOM = function() {
	var trapezi = this, skiniko, kodikos;

	this.theatesDOM.empty();

	skiniko = this.trapeziSkinikoGet();
	if (!skiniko) return this;

	kodikos = this.trapeziKodikosGet();
	skiniko.skinikoSinedriaWalk(function() {
		if (this.sinedriaTrapeziGet() !== kodikos) return;
		if (this.sinedriaOxiTheatis()) return;
		trapezi.trapeziTheatisAddDOM(this.sinedriaPektisGet(), this.sinedriaThesiGet());
	}, true, function(s1, s2) {
		var
		f1 = Kafenio.isFilos(s1.sinedriaPektisGet()),
		f2 = Kafenio.isFilos(s2.sinedriaPektisGet());

		if (f1 && (!f2)) return 1;
		if ((!f1) && f2) return -1;
		if (s1.isodos < s2.isodos) return -1;
		if (s1.isodos > s2.isodos) return 1;
		return 0;
	});

	return this;
};

Trapezi.prototype.trapeziTheatisAddDOM = function(login, thesi) {
	var dom, img;

	dom = this.trapeziTheatisGetDOM(login);
	if (dom) dom.remove();

	if (login.isEgo()) img = 'theatisEgo.png';
	else if (Kafenio.isFilos(login)) img = 'theatisFilos.png';
	else img = 'theatis.png';

	dom = $('<img>').addClass('trapeziTheatis').attr({
		src: Client.server + 'ikona/kafenio/' + img,
		title: login,
	});

	this.theatisDOM[login] = dom;
	this.theatesDOM.prepend(dom);

	return this;
};

Trapezi.prototype.trapeziTheatisGetDOM = function(login) {
	if (!this.hasOwnProperty('theatisDOM')) return undefined;
	return this.theatisDOM[login];
};

Trapezi.prototype.trapeziTheatisDeleteDOM = function(login) {
	var dom;

	dom = this.trapeziTheatisGetDOM(login);
	if (!dom) return this;

	dom.remove();
	delete this.theatisDOM[login];

	return this;
};

Trapezi.prototype.thesiCreateDOM = function(thesi) {
	var
	skiniko = this.trapeziSkinikoGet(),
	dom,
	pektis,
	sinedria,
	trapeziPekti,
	trapezi,
	klasi = 'trapeziPektisBox',
	dom;

	dom = this.trapeziThesiGetDOM(thesi);
	if (dom) dom.remove();

 	pektis = this.trapeziPektisGet(thesi);
	sinedria = skiniko.skinikoSinedriaGet(pektis);

	if (pektis) {
		if (pektis.isEgo()) klasi += ' ego';
		else klasi += ' pektis';
	}

	if (sinedria) {
		trapeziPekti = sinedria.sinedriaTrapeziGet();
		if (trapeziPekti !== this.trapeziKodikosGet()) klasi += ' alitis';
		if (!trapeziPekti) klasi += ' rebelos';
		trapezi = skiniko.skinikoTrapeziGet(trapeziPekti);
		if (trapezi && (!trapezi.trapeziThesiPekti(pektis))) klasi += ' theatis';
	}
	else if (pektis) klasi += ' offline';

	if (!pektis) {
		klasi += ' fevgatos';
		pektis = this.trapeziIxotemisGet(thesi);
		if (pektis) klasi += ' fantasma';
	}
	else if (this.trapeziOxiApodoxi(thesi)) klasi += ' xapodoxi';
	dom = Pektis.pektisLoginDOM(pektis, klasi);

	return(this.thesiDOM[thesi] = dom);
};

// Υπάρχει περίπτωση να μην έχει δημιουργηθεί ακόμη η λίστα "thesiDOM", οπότε
// πρέπει να μεριμνήσουμε ώστε να μην «χτυπήσει» το πρόγραμμα.

Trapezi.prototype.trapeziThesiGetDOM = function(thesi) {
	try {
		return this.thesiDOM[thesi];
	} catch (e) {
		return undefined;
	}
};

Trapezi.prototype.trapeziEkremotitaDOM = function(nai) {
	var dom;

	dom = this.trapeziGetDOM();
	if (!dom) return this;

	if (nai === undefined) nai = true;
	if (nai) dom.addClass('trapeziEkremotita').css('cursor', 'wait');
	else {
		dom.removeClass('trapeziEkremotita');
		setTimeout(function() {
			dom.css('cursor', '');
		}, 500);
	}

	return this;
};

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Sizitisi.zebraPaleta = [
	'#B45F04',
	'#B4045F',
	'#006600',
	'#8A0808',
	'#084B8A',
	'#CD5C5C',
	'#663300',
	'#D52A00',
	'#666699',
];

Sizitisi.zebraIndex = Sizitisi.zebraPaleta.length;

Sizitisi.zebraXroma = {};


Sizitisi.prototype.sizitisiCreateDOM = function(xoros, pro) {
	var pektis, klasi, xroma, dom, sxolioDOM;

	pektis = this.sizitisiPektisGet();
	klasi = 'sizitisiPektis';
	if (pektis.isEgo()) {
		klasi += ' sizitisiPektisEgo';
		xroma = '#144C88';
	}
	else {
		xroma = Sizitisi.zebraXroma[pektis];
		if (!xroma) {
			xroma = Sizitisi.zebraPaleta[Sizitisi.zebraIndex++ % Sizitisi.zebraPaleta.length];
			Sizitisi.zebraXroma[pektis] = xroma;
		}
	}

	dom = pro ? xoros.sizitisiProepiskopisiDOM.empty() : this.DOM = $('<div>');

	dom.addClass('sizitisi').
	append($('<div>').addClass(klasi).css('color', xroma).text(pektis)).
	append(sxolioDOM = $('<div>').addClass('sizitisiSxolio'));
	//append($('<div>').addClass('sizitisiPote').text(this.sizitisiPoteGet()));

	this.sizitisiSxolioCreateDOM(sxolioDOM, xoros);

	if (pro) {
		if (!xoros.sizitisiDOM.data('pagomeno')) xoros.sizitisiDOM.scrollKato();
		return this;
	}

	xoros.sizitisiProepiskopisiDOM.before(this.DOM);
	return this;
};

Sizitisi.prototype.sizitisiGetDOM = function() {
	return this.DOM;
};

Sizitisi.prototype.sizitisiSxolioCreateDOM = function(dom, xoros) {
	var sxolio, tmima, html, i;

	if (xoros === undefined) xoros = Kafenio;

	sxolio = this.sizitisiSxolioGet();
	tmima = sxolio.split('^');

	dom.empty();
	switch (tmima[0]) {
	
	// Αν το πρώτο πεδίο του σχολίου είναι "FP" τότε πρόκειται για τα φύλλα της
	// προηγούμενης διανομής του παίκτη.

	case 'FP':
		sxolio = tmima[1].string2xartosia().xartosiaTaxinomisi().xartosiaDOM();
		dom.append(sxolio);
		return this;
	}

	for (i = 0; i < tmima.length; i++) {
		if (tmima[i].match(/^E[0-9]+:[0-9]+$/)) {
			Sizitisi.emoticonAppend(dom, tmima[i], xoros);
			continue;
		}

		if (tmima[i] === '~') {
			dom.append($('<br />'));
			continue;
		}

		sxolio = tmima[i].replace(/</g, '&lt;');
		dom.append(sxolio);
	}

	return this;
};

Sizitisi.emoticonAppend = function(dom, s, xoros) {
	var tmima, omada, ikona;

	tmima = s.split(':');
	if (tmima.length != 2) return;

	omada = parseInt(tmima[0].replace(/^E/, ''));
	ikona = parseInt(tmima[1]);
	dom.append($('<img>').addClass('sizitisiEmoticon').
	attr('src', Client.server + 'ikona/emoticon/set' + omada + '/' + xoros.epanel.lefkoma[omada][ikona]));
};

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Prosklisi.prototype.prosklisiCreateDOM = function() {
	var prosklisi = this, kodikos, del;

	kodikos = this.prosklisiKodikosGet();
	this.DOM = $('<div>').addClass('prosklisi').

	// Ακολουθεί κώδικας σχετικός με το εικονίδιο ανάκλησης/απόρριψης
	// της προσκλήσεως.

	append(del = $('<img>').addClass('prosklisiIcon').
	attr({src: 'ikona/misc/Xred.png'}).on('click', function(e) {
		Kafenio.inputRefocus(e);
		Client.fyi.pano();
		$(this).working(true);
		Client.skiserService('prosklisiDiagrafi', 'prosklisi=' + kodikos).
		done(function(rsp) {
			del.working(false);
		}).
		fail(function(err) {
			del.working(false);
			Client.skiserFail(err);
		});
	}));

	// Αν η πρόσκληση είναι εξερχόμενη, τότε εμφανίζεται κάπως υποτονική.

	if (this.prosklisiProsGet().oxiEgo()) {
		del.attr({title: 'Ανάκληση πρόσκλησης ' + kodikos});
		this.DOM.append($('<div>').addClass('prosklisiPros').text(this.prosklisiProsGet()));
	}

	// Αλλιώς η πρόσκληση είναι εισερχόμενη και θα εμφανιστεί με εντονότερα
	// χρώματα. Επίσης, θα μπορεί να γίνει αποδοχή.

	else {
		del.attr({title: 'Απόρριψη πρόσκλησης ' + kodikos});
		this.DOM.addClass(this.prosklisiApoGet().oxiEgo() ? 'prosklisiIserxomeni' : 'prosklisiIkothen').
		append($('<div>').addClass('prosklisiApo').text(this.prosklisiApoGet())).
		attr({title: 'Αποδοχή πρόσκλησης ' + kodikos}).on('click', function(e) {
			var trapezi;

			Kafenio.inputRefocus(e);
			if (prosklisi.prosklisiProsGet() !== Client.session.pektis)
			return Client.fyi.epano('Η πρόσκληση αυτή δεν σας αφορά');

			trapezi = Kafenio.skiniko.skinikoTrapeziGet(prosklisi.prosklisiTrapeziGet());
			if (!trapezi) return Client.fyi.epano('Δεν βρέθηκε το τραπέζι');

			if (Kafenio.ego.sinedriaTrapeziGet() === trapezi.trapeziKodikosGet()) {
				if (trapezi.trapeziThesiPekti(Kafenio.ego.sinedriaPektisGet()))
				return Client.fyi.epano("Παίζετε ήδη σ' αυτό το τραπέζι");

				if (!trapezi.trapeziKeniThesi())
				return Client.fyi.epano("Δεν υπάρχει κενή θέση σ' αυτό το τραπέζι");
			}

			Client.fyi.pano();
			del.working(true);
			Client.skiserService('prosklisiApodoxi', 'prosklisi=' + kodikos).
			done(function(rsp) {
				del.working(false);
			}).
			fail(function(err) {
				del.working(false);
				Client.skiserFail(err);
			});
		});
	}

	this.DOM.append($('<div>').addClass('prosklisiTrapezi').text(this.prosklisiTrapeziGet()));
	Kafenio.prosklisiBaseDOM.after(this.DOM);
	return this;
};

Prosklisi.prototype.prosklisiGetDOM = function() {
	return this.DOM;
};

Prosklisi.prototype.prosklisiDeleteDOM = function() {
	var dom;

	dom = this.prosklisiGetDOM();
	if (dom) dom.remove();

	return this;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// Η μέθοδος "pektisEntopismosDOM" δέχεται ως παράμετρο το login name κάποιου παίκτη
// και ενημερώνει την εμφάνιση του συγκεκριμένου παίκτη στην περιοχή τραπεζιών.

Skiniko.prototype.pektisEntopismosDOM = function(pektis) {
	var sinedria, sinedriaDOM, trapeziPekti, trapezi, theatis;

	// Αν δεν έχει περαστεί παίκτης δεν προβαίνουμε σε καμία απολύτως ενέργεια.

	if (!pektis) return this;

	// Εντοπίζουμε τη συνεδρία του παίκτη (εφόσον υπάρχει) και το τραπέζι που
	// αναφέρεται στη συγκεκριμένη συνεδρία.

	sinedria = this.skinikoSinedriaGet(pektis);
	if (sinedria) {
		trapeziPekti = sinedria.sinedriaTrapeziGet();
		sinedriaDOM = sinedria.sinedriaGetDOM().removeClass('pektis theatis');
		Pektis.pektisSxesiRefreshDOM(pektis, sinedriaDOM);
	}
	else {
		trapeziPekti = undefined;
		sinedriaDOM = undefined;
	}

	// Μπορεί ο παίκτης να μην έχει συνεδρία, επομένως πάμε αντίστροφα για να
	// διαπιστώσουμε αν είναι παίκτης, θεατής ή ρέμπελος.

	if (trapeziPekti) trapezi = this.skinikoTrapeziGet(trapeziPekti);
	theatis = trapezi && (!trapezi.trapeziThesiPekti(pektis));

	// Αν ο παίκτης είναι ο ίδιος ο χρήστης αφαιρούμε την απεικόνιση του τρέχοντος
	// τραπεζιού προκειμένου να κάνουμε εκ νέου εμφανές το τρέχον τραπέζι. Επίσης
	// αφαιρούμε την απεικόνιση τραπεζιού θέασης σε περίπτωση που ο χρήστης είναι
	// θεατής.

	if (pektis.isEgo()) {
		if (sinedriaDOM) sinedriaDOM.addClass('ego');
		$('.trapezi').removeClass('trapeziPeximo trapeziTheasi');
		if (trapezi) trapezi.trapeziGetDOM().addClass(theatis ? 'trapeziTheasi' : 'trapeziPeximo');
	}
	else if (sinedriaDOM) {
		if (sinedria.sinedriaIsPektis()) sinedriaDOM.addClass('pektis');
		else if (sinedria.sinedriaIsTheatis()) sinedriaDOM.addClass('theatis');
	}

	// Διατρέχουμε όλες τις θέσεις παικτών όλων των τραπεζιών και ενημερώνουμε
	// τις θέσεις του συγκεκριμένου παίκτη.

	this.skinikoThesiWalk(function(thesi) {
		var thesiDOM;

		if (this.trapeziPektisGet(thesi) !== pektis) return;

		thesiDOM = this.trapeziThesiGetDOM(thesi);
		if (!thesiDOM) return;

		Pektis.pektisSxesiRefreshDOM(pektis, thesiDOM);

		if (sinedria) thesiDOM.removeClass('offline');
		else thesiDOM.addClass('offline');
		
		if (this.trapeziKodikosGet() === trapeziPekti) thesiDOM.removeClass('alitis');
		else thesiDOM.addClass('alitis');

		if (trapeziPekti) thesiDOM.removeClass('rebelos');
		else thesiDOM.addClass('rebelos');

		if (theatis) thesiDOM.addClass('theatis');
		else thesiDOM.removeClass('theatis');
	});

	// Αν ο παίκτης φαίνεται να είναι θεατής σε κάποιο τραπέζι, τότε πρέπει
	// να τον εμφανίσουμε στο κάτω μέρος του τραπεζιού.

	if (trapezi && sinedria && sinedria.sinedriaIsTheatis())
	trapezi.trapeziTheatisAddDOM(pektis, sinedria.sinedriaThesiGet());

	return this;
};

Skiniko.prototype.pektisTrapeziScroll = function(anim) {
	var trapezi, cdom, tdom, scrollTop;

	trapezi = this.skinikoTrapeziGet(Kafenio.ego.sinedriaTrapeziGet());
	if (!trapezi) return this;

	cdom = Kafenio.trapeziaDOM;
	tdom = trapezi.trapeziGetDOM();
	scrollTop = tdom.offset().top - cdom.offset().top + cdom.scrollTop() - 4;
	cdom.finish();
	if (anim) cdom.animate({scrollTop: scrollTop}, 100);
	else cdom.scrollTop(scrollTop);
	return this;
};

Skiniko.prototype.skinikoTheatisDeleteDOM = function(login) {
	var sinedria;

	sinedria = this.skinikoSinedriaGet(login);
	if (!sinedria) return this;
	if (sinedria.sinedriaOxiTheatis()) return this;
	sinedria.sinedriaTheatisDeleteDOM();

	if (Kafenio.oxiTsoxa()) return this;
	if (Tsoxa.oxiTrapezi(sinedria.sinedriaTrapeziGet())) return this;
	sinedria.sinedriaTsoxaTheatisDeleteDOM();

	return this;
};

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Pektis.pektisLoginDOM = function(login, klasi) {
	var dom, isalk = 'pektisBox';

	if (klasi !== undefined) isalk += ' ' + klasi;
	dom = $('<div>').addClass(isalk);

	switch (login) {
	case undefined:
	case null:
		dom.attr({
			title: 'κενή θέση',
		}).append($('<div>').addClass('pektisLogin').html('&mdash;'));
		break;
	default:
		dom.attr({
			title: login,
		}).append(
			$('<div>').addClass('pektisLogin').text(login).
			on('mouseenter', function(e) {
				e.stopPropagation();
				$(this).addClass('pektisLoginEntono');
			}).
			on('mouseleave', function(e) {
				e.stopPropagation();
				$(this).removeClass('pektisLoginEntono');
			})
		);
		Pektis.pektisSxesiRefreshDOM(login, dom);
		break;
	}

	return dom;
};

Pektis.pektisSxesiRefreshDOM = function(login, dom) {
	if (!dom) return;

	dom.
	removeClass('sxesiFilos sxesiApoklismenos').
	find('.sxesiFilosMark').remove();

	if (Kafenio.isFilos(login))
	dom.
	addClass('sxesiFilos').
	append($('<div>').addClass('sxesiFilosMark'));

	else if (Kafenio.isApoklismenos(login))
	dom.addClass('sxesiApoklismenos');
};

Pektis.prototype.pektisProfinfoDOM = function(xoros, win) {
	var login = this.pektisLoginGet(), panel;

	if (win === undefined) win = window;
	xoros.profinfoDOM[login] = $('<div>').addClass('profinfo').
	css(xoros.profinfoOffset[login]).siromeno(null, win).anadisi().
	append(panel = $('<div>').addClass('profinfoPanel')).
	on('remove', function() {
		var pos = xoros.profinfoDOM[login].position();
		delete xoros.profinfoDOM[login];
		xoros.profinfoOffset[login] = {
			top: pos.top,
			left: pos.left,
		};
	}).append(
		win.Client.klisimo(function() {
			xoros.profinfoDOM[login].remove();
		})
	).appendTo(xoros.ofelimoDOM);

	panel.
	on('mousedown', function(e) {
		e.stopPropagation();
	});

	Pektis.profinfoPanelRefreshDOM(login, xoros, win);
	return this;
};

Pektis.profinfoPanelRefreshDOM = function(login, xoros, win) {
	var panel = xoros.profinfoDOM[login].find('.profinfoPanel');

	panel.empty().
	append($('<button>').text('Πρόσκληση').on('click', function(e) {
		var trapeziKodikos, trapezi, button = $(this);

		xoros.inputRefocus(e);
		if ($(this).data('apostoli')) return;

		trapeziKodikos = Kafenio.ego.sinedriaTrapeziGet();
		if (!trapeziKodikos) return win.Client.fyi.epano('Απροσδιόριστο τραπέζι');
		if (Kafenio.ego.sinedriaOxiPektis()) return win.Client.fyi.epano('Δεν είστε παίκτης στο τραπέζι');

		trapezi = Kafenio.skiniko.skinikoTrapeziGet(trapeziKodikos);
		if (!trapezi) return win.Client.fyi.epano('Ακαθόριστο τραπέζι');

		win.Client.fyi.pano('Αποστολή πρόσκλησης. Παρακαλώ περιμένετε…');
		win.Client.skiserService('prosklisiApostoli', 'pros=' + login.uri()).
		done(function(rsp) {
			button.removeData('apostoli');
			win.Client.fyi.pano();
			xoros.profinfoDOM[login].remove();
		}).
		fail(function(err) {
			button.removeData('apostoli');
			win.Client.skiserFail(err);
		});
	})).

	append($('<button>').text('Μήνυμα'));

	if (Kafenio.isFilos(login)) panel.append($('<img>').addClass('profinfoIcon').attr({
		src: Client.server + 'ikona/misc/Xgreen.png',
		title: 'Ανάκληση φιλίας',
	}).on('click', function(e) {
		Kafenio.alagiSxesis(e, login, null, xoros, win);
	}));
	else panel.append($('<img>').addClass('profinfoIcon').attr({
		src: Client.server + 'ikona/misc/filos.png',
		title: 'Φίλος',
	}).on('click', function(e) {
		Kafenio.alagiSxesis(e, login, 'ΦΙΛΟΣ', xoros, win);
	}));

	if (Kafenio.isApoklismenos(login)) panel.append($('<img>').addClass('profinfoIcon').attr({
		src: Client.server + 'ikona/misc/Xred.png',
		title: 'Άρση αποκλεισμού',
	}).on('click', function(e) {
		Kafenio.alagiSxesis(e, login, null, xoros, win);
	}));
	else panel.append($('<img>').addClass('profinfoIcon').attr({
		src: Client.server + 'ikona/misc/apoklismos.png',
		title: 'Αποκλεισμός',
	}).on('click', function(e) {
		Kafenio.alagiSxesis(e, login, 'ΑΠΟΚΛΕΙΣΜΕΝΟΣ', xoros, win);
	}));

	return this;
};
